home *** CD-ROM | disk | FTP | other *** search
Wrap
/* File: SoundLevel.c Contains: A simple application that echos the sound input level on the microphone using a display much like a VCR peak-hold bar meter. Its a fun app, to use up those wasted background cycles and seems to amuse people to no end. At 40k, its not a major memory hog so pop it in your Startup Items folder and enjoy. The About Box introduces a new power user short cut. Clap hard enough to raise the sound threshold about ≈ 95% and the dialog will dismiss. No need to reach and strain for that enter key anymore! Caveats: • Not much error checking. • Too many globals. • Saves the window position back to the app resource fork. • Requires a Sound Input Device (MacRecorder will do just fine) • It keeps the sound input device open with r/w so you won’t be able to use other sound input apps unless you quit. Have fun and let me know what you think. Written by: Ken Bereskin Copyright: Copyright © 1992-1999 by Apple Computer, Inc., All Rights Reserved. You may incorporate this Apple sample source code into your program(s) without restriction. This Apple sample source code has been provided "AS IS" and the responsibility for its operation is yours. You are not permitted to redistribute this Apple sample source code as "Apple sample source code" after having made changes. If you're going to re-distribute the source, we require that you make it clear in the source that the code was descended from Apple sample source code, but that you've made changes. Change History (most recent first): 8/2/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1 */ /* --------------------------------------------------------------------------------- ToolBox header files */ #include <Types.h> #include <QuickDraw.h> #include <Resources.h> #include <Windows.h> #include <Fonts.h> #include <Events.h> #include <TextEdit.h> #include <Dialogs.h> #include <Menus.h> #include <Memory.h> #include <Desk.h> #include <ToolUtils.h> #include <OSUtils.h> #include <Errors.h> #include <Folders.h> #include <OSEvents.h> #include <SegLoad.h> #include <Traps.h> #include <SoundInput.h> #include <GestaltEqu.h> /* --------------------------------------------------------------------------------- Forward reference prototypes */ void Initialize(void); void SetupEnvironment(void); void SetupMemory(void); void SetupMenus(void); void DoEvents(void); void DoMouseDown (EventRecord *event); void DoKeyDown (EventRecord *event); void DoUpdateWindow (WindowPtr window); void DoActivateWindow (Boolean activate, WindowPtr window); void DoMenuCommand(short theMenu, short theItem); void DoContentClick(EventRecord *event, WindowPtr window); void DoSuspend(EventRecord *event); void DoResume(EventRecord *event); void DoIdle(void); void DoDeskAccessory(short whichMenu, short whichItem); void NewLevelWindow(void); void CleanUp(void); void DoAbout(void); Boolean OptionTest(void); Boolean CmdTest(void); Boolean ShiftTest(void); Boolean ColourQDExists (void); Boolean ColourQDIsOn (void); short ColourDepth (void); void TwitchToFinder(void); void DrawTheMeter(Rect *meterRect, short numElements, short *peakLevel, short *lastLevel, long *peakTimeout, short meterLevel, short redZone, RGBColor *blueRGB, RGBColor *redRGB, RGBColor *blackRGB, Boolean useColour); void GetBarRect(Rect *meterRect, short whichBar, Rect *barRect); void InitLevelMeter (void); void CloseLevelMeter (void); OSErr OpenTheSoundDevice(void); void DrawOneBar(Rect *meterRect, short whichBar, Boolean useColour); pascal Boolean AboutFilterProc(DialogPtr dialog, EventRecord *event, short *item); void HiliteItem(DialogPtr dialog, short theitem); void UpdateAboutDialog(WindowPtr window); void MakeCursor(short cursorID); /* --------------------------------------------------------------------------------- Constants */ #define kMaxVolume 255 #define kPeakTimeoutTicks 60 // number of ticks needed to timeout peak #define kMenuBarID 128 #define kAppleMenu 128 #define kAboutItem 1 #define kFileMenu 129 #define kQuitItem 1 #define kGestaltLevelMeter 'SnLv' /* --------------------------------------------------------------------------------- Global variables */ Boolean WNEIsImplemented; // is WaitNextEvent avaiable Boolean gQuitFlag = false; // set to force a quit WindowPtr gLevelWindow; // sound level window pointer RGBColor blueRGB, redRGB, blackRGB; // rgb colours used for drawing meter short gNumElements; // number of elements in the meter long gPeakTimeOut; short gPeakLevel; short gLastLevel; short gRedZone; Rect gMeterRect; long gSoundInputRefNum; Boolean gUseColour; #ifdef powerc QDGlobals qd; #endif /* --------------------------------------------------------------------------------- Main Application main entry point */ void main() { Initialize(); // do application initialization NewLevelWindow(); // create the level window InitLevelMeter(); // start up the level meter DoUpdateWindow(gLevelWindow); while (!gQuitFlag) // handle events until quit DoEvents(); CleanUp(); // clean up before quitting } /* --------------------------------------------------------------------------------- Intialize First time application initialization */ #pragma segment Initialize static void Initialize() { InitGraf(&qd.thePort); InitFonts(); InitWindows(); InitMenus(); TEInit(); InitDialogs(nil); InitCursor(); FlushEvents(everyEvent, 0); SetupMemory(); SetupEnvironment(); SetupMenus(); gUseColour = ColourDepth() >= 4; } /* --------------------------------------------------------------------------------- SetupEnvironment This routine will determine the hardware and software operating environment */ #define RequestedVersion 1 static void SetupEnvironment() { WNEIsImplemented = true; } /* --- SetupMemory ------------------------------------------------------------------------------------ This routine will allocate some master pointers, grow the heap, and set up a grow zone function. */ static void SetupMemory() { MaxApplZone(); MoreMasters(); MoreMasters(); } /* --- SetupMenus ------------------------------------------------------------------------------------ This routine will set up the LeadSheet menus. */ static void SetupMenus() { Handle menuBarHndl; // load up the apple and file menu menuBarHndl = GetNewMBar(kMenuBarID); // read menus into menu bar SetMenuBar(menuBarHndl); // install menus DisposeHandle(menuBarHndl); AppendResMenu(GetMenuHandle(kAppleMenu), 'DRVR'); // add DA names to Apple menu DrawMenuBar(); } #pragma segment Main /* --------------------------------------------------------------------------------- DoEvents This is the LaunchPad Main Event Loop. We fetch events from the event queue, and call the proper handlers for the given event class. */ #define kSleepTicks 0 static void DoEvents() { Boolean newEvent; // is there an event in the queue EventRecord event; // the event record /* Look for an event in the event queue. */ if (WNEIsImplemented) { newEvent = WaitNextEvent(everyEvent, &event, kSleepTicks, nil); } else { SystemTask(); newEvent = GetNextEvent(everyEvent, &event); } DoIdle(); // idle processing /* Dispatch on the type of event */ switch(event.what) { case mouseDown: DoMouseDown(&event); break; case keyDown: case autoKey: DoKeyDown(&event); break; case updateEvt: DoUpdateWindow((WindowPtr)event.message); break; case activateEvt: DoActivateWindow(event.modifiers & activeFlag, (WindowPtr)event.message); break; case diskEvt: break; case osEvt: /* Check for suspend or resume events (the only ones now defined) */ if ((event.message >> 24) == suspendResumeMessage) { if (event.message & resumeFlag) DoResume(&event); else DoSuspend(&event); } break; case highLevelEventMask: break; } DoIdle(); // more idle processing } /* --- DoMouseDown ---------------------------------------------------------------------------------- This routine handles mouseDown events. */ static void DoMouseDown(event) EventRecord *event; { short whichPart; WindowPtr window; Rect dragBounds; long selection; short theMenu, theItem; /* Determine where the mouseDown event occurred */ whichPart = FindWindow(event->where, &window); switch(whichPart) { case inMenuBar: selection = MenuSelect(event->where); theMenu = HiWord(selection); theItem = LoWord(selection); DoMenuCommand(theMenu, theItem); break; case inSysWindow: SystemClick(event, window); break; case inContent: DoContentClick(event, window); break; case inDrag: dragBounds = qd.screenBits.bounds; DragWindow(window, event->where, &dragBounds); break; case inGrow: break; case inGoAway: break; case inZoomIn: case inZoomOut: break; case inDesk: // a click in the desktop will deselect the current selection break; } } /* --------------------------------------------------------------------------------- DoKeyDown This routine handles keyDown events. */ static void DoKeyDown (EventRecord *event) { #pragma unused(event) gQuitFlag = true; } /* --------------------------------------------------------------------------------- DoUpdateWindow This routine handles update events for the given window */ static void DoUpdateWindow (window) WindowPtr window; { GrafPtr savePort; Rect boundsRect; short i; GetPort(&savePort); BeginUpdate(window); // set up the clipRgn and visRgn SetPort(window); RGBForeColor(&blackRGB); EraseRect(&window->portRect); boundsRect = window->portRect; InsetRect(&boundsRect, 2, 2); PaintRect(&boundsRect); // redraw the bar graph frames up to the current level for (i = 1; i <= gLastLevel; i++) { DrawOneBar(&gMeterRect, i, gUseColour); } EndUpdate(window); // restore window regions SetPort(savePort); } /* --- DoActivateWindow ---------------------------------------------------------------------------------- This routine handles activate events for the given window */ static void DoActivateWindow (Boolean activate,WindowPtr window) { #pragma unused(activate, window) } /* --------------------------------------------------------------------------------- DoMenuCommand This routine handles menu selection dispatching */ static void DoMenuCommand(short theMenu, short theItem) { switch (theMenu) { case kAppleMenu: if (theItem == kAboutItem) { DoAbout(); } else { DoDeskAccessory(theMenu, theItem); } break; case kFileMenu: if (theItem == kQuitItem) { ExitToShell(); } break; default: break; } HiliteMenu(0); } /* --- DoContentClick ------------------------------------------------------------------------------ This routine handles clicks in the content area of a window. If the option key is down, drag the launch window to another location on the screen. */ static void DoContentClick(event, window) EventRecord *event; WindowPtr window; { #pragma unused(event) GrafPtr savePort; Point clickPt; /* If the clicked window isn't frontmost, simply select it */ if (window != FrontWindow()) { SelectWindow(window); return; } GetPort(&savePort); // save the current port SetPort(window); // set the port to the clicked window clickPt = event->where; // convert point to local coordindates GlobalToLocal(&clickPt); DragWindow(window, event->where, &qd.screenBits.bounds); SetPort(savePort); // restore the saved port } /* --------------------------------------------------------------------------------- DoSuspend This routine handles multifinder suspend events. */ static void DoSuspend(event) EventRecord *event; { #pragma unused(event) DoActivateWindow(false, gLevelWindow); } /* --------------------------------------------------------------------------------- DoResume This routine handles multifinder resume events. */ static void DoResume(event) EventRecord *event; { #pragma unused(event) InitCursor(); DoActivateWindow(true, gLevelWindow); } /* --------------------------------------------------------------------------------- DoDeskAccessory Handle desk accessory selections from the apple menu */ static void DoDeskAccessory(short whichMenu,short whichItem) { Str255 daName; GetMenuItemText(GetMenuHandle(whichMenu), whichItem, daName); (void) OpenDeskAcc(daName); } /* --------------------------------------------------------------------------------- NewLevelWindow This routine will create the sound level window. */ static void NewLevelWindow() { Point **userOffsetPtHndl; Rect screenRect; Rect globalRect; if (ColourQDExists()) gLevelWindow = GetNewCWindow(128, nil, (WindowPtr)-1); else gLevelWindow = GetNewWindow(128, nil, (WindowPtr)-1); SetPort(gLevelWindow); // load the offset resource that tells us if the user has dragged // the window somewhere else. make sure the window remains visible // and eventually put this in a pref file where it belongs userOffsetPtHndl = (Point **)GetResource('WPos', 128); if (userOffsetPtHndl != nil) { screenRect = qd.screenBits.bounds; InsetRect(&screenRect, 5, 5); // a little bit of a margin // the port is set for our window, so convert (0,0) to global for the // global topLeft of the window globalRect.left = (**userOffsetPtHndl).h; globalRect.top = (**userOffsetPtHndl).v; globalRect.right = globalRect.left + (gLevelWindow->portRect.right - gLevelWindow->portRect.left); globalRect.bottom = globalRect.top + (gLevelWindow->portRect.bottom - gLevelWindow->portRect.top); if (SectRect(&screenRect, &globalRect, &globalRect)) { MoveWindow(gLevelWindow, globalRect.left, globalRect.top, true); } ReleaseResource((Handle)userOffsetPtHndl); } ShowWindow(gLevelWindow); // make it visible } /* --------------------------------------------------------------------------------- CleanUp This routine will clean up before exiting to the Finder */ static void CleanUp() { Point **userOffsetPtHndl; Point globalPt; CloseLevelMeter(); // close up the level meter // save the position of the top/left corner of the window in case the user // has moved it. For now, back to the WPos resource in the app will have to do userOffsetPtHndl = (Point **)GetResource('WPos', 128); if (userOffsetPtHndl != nil) { SetPort(gLevelWindow); globalPt.h = globalPt.v = 0; LocalToGlobal(&globalPt); **userOffsetPtHndl = globalPt; ChangedResource((Handle)userOffsetPtHndl); WriteResource((Handle)userOffsetPtHndl); } if (gLevelWindow != nil) DisposeWindow(gLevelWindow); } /* --------------------------------------------------------------------------------- MakeCursor This routine will set the cursor to the 'CURS' resource whose id is given. */ void MakeCursor(short cursorID) { #pragma unused(cursorID) } /* --------------------------------------------------------------------------------- DoIdle */ static void DoIdle() { OSErr err; short recordingStatus = 0; // status of recording session short meterLevel = 0; // current meter level unsigned long totalSamplesToRecord = 0; // total number of samples unsigned long numberOfSamplesRecorded = 0; // number of samples recorded unsigned long totalMsecsToRecord; unsigned long numberOfMsecsRecorded; GrafPtr savePort; GetPort(&savePort); SetPort(gLevelWindow); /* get the sound input status */ err = SPBGetRecordingStatus(gSoundInputRefNum, &recordingStatus, &meterLevel, &totalSamplesToRecord, &numberOfSamplesRecorded, &totalMsecsToRecord, &numberOfMsecsRecorded); DrawTheMeter(&gMeterRect, gNumElements, &gPeakLevel, &gLastLevel, &gPeakTimeOut, meterLevel, gRedZone, &blueRGB, &redRGB, &blackRGB, gUseColour); SetPort(savePort); } /* --------------------------------------------------------------------------------- OptionTest Returns whether the option key is being pressed */ static Boolean OptionTest() { KeyMap theKeys; GetKeys(theKeys); if (theKeys[1] & 4) return(true); else return(false); } /* --------------------------------------------------------------------------------- CmdTest Returns whether the command (apple) key is being pressed */ static Boolean CmdTest() { KeyMap theKeys; GetKeys(theKeys); if (theKeys[1] & 0x8000) return(true); else return(false); } /* --------------------------------------------------------------------------------- ShiftTest Returns whether the shift key is being pressed */ static Boolean ShiftTest() { KeyMap theKeys; GetKeys(theKeys); if (theKeys[1] & 1) return(true); else return(false); } #define ROM85 (*(short *)0x28e) #define TWOHIGHMASK 0xc000 #define COLOURQDEXISTS !(ROM85 & TWOHIGHMASK) /* --------------------------------------------------------------------------------- ColourQDExists This routine is called to determine whether or not color quickdraw exists in the current roms. It checks the lom memory global just like Apple usually does… */ Boolean ColourQDExists() { return(COLOURQDEXISTS); } /* --------------------------------------------------------------------------------- ColourQDIsOn This routine is called to determine if colour quickdraw exists in the current roms and if the main screen is displaying color. */ #define QD_MIN_FOR_COLOUR 4 Boolean ColourQDIsOn() { GDHandle maindevice; PixMapHandle mainpix; short depth; /* First confirm that color is available in the roms */ if (COLOURQDEXISTS) { /* Determine if the main device is displaying color */ maindevice = GetMainDevice(); mainpix = (*maindevice)->gdPMap; /* Check the depth of the pixel map */ depth = (*mainpix)->pixelSize; if (depth < QD_MIN_FOR_COLOUR) return(false); else return(true); } else return(false); } short ColourDepth() { GDHandle maindevice; PixMapHandle mainpix; /* First confirm that color is available in the roms */ if (COLOURQDEXISTS) { /* Determine if the main device is displaying color */ maindevice = GetMainDevice(); mainpix = (*maindevice)->gdPMap; /* Check the depth of the pixel map */ return (*mainpix)->pixelSize; } else return 1; } /* --------------------------------------------------------------------------------- TwitchToFinder Switch back to Finder. Use the System 7.0 process manager if present */ static void TwitchToFinder() { } /* --------------------------------------------------------------------------------- DrawTheMeter This routine is called to draw the volume meter during sound input. */ void DrawTheMeter(Rect *meterRect, short numElements, short *peakLevel, short *lastLevel, long *peakTimeout, short meterLevel, short redZone, RGBColor *blueRGB, RGBColor *redRGB, RGBColor *blackRGB, Boolean useColour) { Rect barRect; short i; PenNormal(); // quantize the meterLevel based on number of elements // in our bar chart if (meterLevel > kMaxVolume) meterLevel = kMaxVolume; meterLevel = (meterLevel * numElements) / kMaxVolume; // determine if this is a new peak value and erase the // peak bar if it has timed out if (meterLevel >= *peakLevel) { *peakLevel = meterLevel; *peakTimeout = TickCount() + kPeakTimeoutTicks; } else { // has the current peak timed out? if (*peakLevel > 0 && *peakTimeout <= TickCount()) { if (*peakLevel >= *lastLevel) { if (useColour) RGBForeColor(blackRGB); GetBarRect(meterRect, *peakLevel, &barRect); PaintRect(&barRect); // erase the previous element (peak is two elements wide) if (*peakLevel > 1) { // don’t erase a non-element GetBarRect(meterRect, *peakLevel - 1, &barRect); PaintRect(&barRect); } } *peakLevel = 0; } } // check whether the signal is now stronger than last time if (meterLevel > *lastLevel) { for (i = *lastLevel + 1; i <= meterLevel; i++) { GetBarRect(meterRect, i, &barRect); // get the rect for this bar if (useColour) { // watch out for colour if (i >= redZone) // are we beyond the clipping point? RGBForeColor(redRGB); // draw in red to show distortion else RGBForeColor(blueRGB); // draw in blue for normal signal PaintRect(&barRect); // fill the element in } else EraseRect(&barRect); // no colour so white will have to do } *lastLevel = meterLevel; // remember for next time } // check whether the signal level is now weaker than last time // and if it is remove the rightmost two level bar currently on if (meterLevel < *lastLevel) { if (*peakLevel != 0) { if (*lastLevel == *peakLevel) // don’t erase the peak! *lastLevel -= 2; if (*lastLevel == *peakLevel -1) *lastLevel -= 1; } if (useColour) RGBForeColor(blackRGB); for (i = 0; i < 2; i++) { if (*lastLevel > 0) { GetBarRect(meterRect, *lastLevel, &barRect); PaintRect(&barRect); *lastLevel -= 1; } if (*lastLevel < 0 ) *lastLevel = 0; } } if (useColour) RGBForeColor(blackRGB); } /* --------------------------------------------------------------------------------- GetBarRect */ void GetBarRect(Rect *meterRect, short whichBar, Rect *barRect) { if (whichBar == 0) { return; } SetRect(barRect, meterRect->left + 2, meterRect->top + 1, meterRect->left + 4, meterRect->bottom - 1); OffsetRect(barRect, (whichBar - 1)* 3, 0); } /* ------------------------------------------------------------------------------- InitLevelMeter */ void InitLevelMeter() { RGBColor **colorRsrcHndl; OSErr err; // load the colours used for the bar chart colorRsrcHndl = (RGBColor **)GetResource(128, 'RGBv'); if (colorRsrcHndl) { blueRGB = **colorRsrcHndl; ReleaseResource((Handle)colorRsrcHndl); } else { blueRGB.red = 39321; // flourescant blue blueRGB.green = 65535; blueRGB.blue = 65535; } colorRsrcHndl = (RGBColor **)GetResource(129, 'RGBv'); if (colorRsrcHndl) { redRGB = **colorRsrcHndl; ReleaseResource((Handle)colorRsrcHndl); } else { redRGB.red = 65535; // peak level red redRGB.green = 0; redRGB.blue = 0; } blackRGB.red = 0; blackRGB.green = 0; blackRGB.blue = 0; gMeterRect = gLevelWindow->portRect; InsetRect(&gMeterRect, 2, 2); gNumElements = (gMeterRect.right - gMeterRect.left - 2) / 3; if (gNumElements > 255) gNumElements = 255; gPeakTimeOut = 0; gPeakLevel = 0; gLastLevel = 0; gRedZone = (gNumElements * 2) / 3; // open the sound input device err = OpenTheSoundDevice(); } // watch out for other usages of the soundInputRefNum static void CloseLevelMeter() { OSErr err; err = SPBCloseDevice(gSoundInputRefNum); gSoundInputRefNum = 0; } #define kAboutDLOG 128 #define kAboutMeterItem 4 short gAboutPeakLevel; short gAboutLastLevel; long gAboutPeakTimeOut; void DoAbout() { DialogPtr dialog; short itemHit; GrafPtr savePort; ModalFilterUPP aboutFilterProcUPP; /* create a UPP for our modal dialog filter */ aboutFilterProcUPP = NewModalFilterProc(AboutFilterProc); GetPort(&savePort); dialog = GetNewDialog(kAboutDLOG, nil, (WindowPtr)-1); SetPort(dialog); // ••• force an update event first UpdateAboutDialog((WindowPtr)dialog); gAboutPeakLevel = 0; gAboutLastLevel = 0; gAboutPeakTimeOut = 0; do { ModalDialog(aboutFilterProcUPP, &itemHit); } while (itemHit != ok); SetPort(savePort); DisposeDialog(dialog); DisposeRoutineDescriptor(aboutFilterProcUPP); } OSErr OpenTheSoundDevice() { OSErr err; short meterState; // set the default zone to the system heap so that the SI manager will // possibly get fooled into allocating there instead of my heap SetZone(SystemZone()); err = SPBOpenDevice(nil, siWritePermission, &gSoundInputRefNum); if (err != noErr) goto BailOut; // turn on sound metering meterState = 1; // turn it on err = SPBSetDeviceInfo(gSoundInputRefNum, siLevelMeterOnOff, (char *)&meterState); BailOut: SetZone(ApplicationZone()); return err; } void DrawOneBar(Rect *meterRect, short whichBar, Boolean useColour) { Rect barRect; GetBarRect(meterRect, whichBar, &barRect); // get the rect for this bar if (useColour) { // watch out for colour if (whichBar >= gRedZone) // are we beyond the clipping point? RGBForeColor(&redRGB); // draw in red to show distortion else RGBForeColor(&blueRGB); // draw in blue for normal signal PaintRect(&barRect); // fill the element in } else EraseRect(&barRect); // no colour so white will have to do } /* --------------------------------------------------------------------------------- AboutFilterProc */ #define CRKey 0x0d #define EnterKey 0x03 #define EscapeKey 0x1b pascal Boolean AboutFilterProc(DialogPtr dialog, EventRecord *event, short *item) { unsigned char theKey; WindowPtr window; short itemType; Handle itemHndl; Rect itemRect; OSErr err; short recordingStatus = 0; // status of recording session short meterLevel = 0; // current meter level unsigned long totalSamplesToRecord = 0; // total number of samples unsigned long numberOfSamplesRecorded = 0; // number of samples recorded unsigned long totalMsecsToRecord; unsigned long numberOfMsecsRecorded; /* get the sound input status */ if (event->what != updateEvt) { err = SPBGetRecordingStatus(gSoundInputRefNum, &recordingStatus, &meterLevel, &totalSamplesToRecord, &numberOfSamplesRecorded, &totalMsecsToRecord, &numberOfMsecsRecorded); GetDialogItem(dialog, kAboutMeterItem, &itemType, &itemHndl, &itemRect); InsetRect(&itemRect, 2, 2); SetPort(dialog); DrawTheMeter(&itemRect, gNumElements, &gAboutPeakLevel, &gAboutLastLevel, &gAboutPeakTimeOut, meterLevel, gRedZone, &blueRGB, &redRGB, &blackRGB, gUseColour); // check for nearly maximum volume to dismiss dialog; if (meterLevel > 250) { HiliteItem(dialog, ok); *item = ok; return true; } } switch(event->what) { case mouseDown: break; case keyDown: case autoKey: theKey = event->message & charCodeMask; if (theKey == CRKey || theKey == EnterKey || theKey == EscapeKey) { HiliteItem(dialog, ok); *item = ok; return true; } break; case updateEvt: window = (WindowPtr)event->message; if (window == (WindowPtr)dialog) { UpdateAboutDialog(window); } break; default: break; } return false; } void UpdateAboutDialog(WindowPtr window) { GrafPtr savePort; short itemType; Handle itemHndl; Rect itemRect; BeginUpdate(window); GetPort(&savePort); SetPort(window); UpdateDialog((DialogPtr)window, window->visRgn); PenNormal(); GetDialogItem((DialogPtr)window, kAboutMeterItem, &itemType, &itemHndl, &itemRect); FrameRect(&itemRect); InsetRect(&itemRect, 2, 2); PaintRect(&itemRect); SetPort(savePort); EndUpdate(window); } /* --------------------------------------------------------------------------------- HiliteItem This routine will hilite the given item control so the user knows which item has been activated by the key equivalent */ #define ButtonInvertState 10 // inverted state for push buttons #define CheckRadioState 11 // inverted state for checks and radios #define NormalState 0 // non-inverted state #define SleepDuration 10 // pause for 10 ticks void HiliteItem(DialogPtr dialog, short theitem) { Handle thehndl; short thetype; Rect thebox; unsigned long junkTicks; short hiliteState; /* Get Handle to the item */ GetDialogItem(dialog, theitem, &thetype, &thehndl, &thebox); switch (thetype) { case btnCtrl + ctrlItem: hiliteState = ButtonInvertState; break; case chkCtrl + ctrlItem: case radCtrl + ctrlItem: hiliteState = CheckRadioState; break; default: return; break; } /* Invert the control for a little while */ HiliteControl((ControlHandle)thehndl, hiliteState); Delay(SleepDuration, &junkTicks); HiliteControl((ControlHandle)thehndl, NormalState); }